#include "stdafx.h"
#include "ParseCoverageData.h"

const wstring SOURCEFILENAMES = L"<SourceFileNames>";
const wstring SOURCEFILENAMESEND = L"</SourceFileNames>";
const wstring SOURCEFILEID = L"<SourceFileID>";
const wstring SOURCEFILEIDEND = L"</SourceFileID>";
const wstring SOURCEFILENAME = L"<SourceFileName>";
const wstring SOURCEFILENAMEEND = L"</SourceFileName>";

const wstring METHOD = L"<Method>";
const wstring METHODEND = L"</Method>";
const wstring LINESCOVERED = L"<LinesCovered>";
const wstring LINESCOVEREDEND = L"</LinesCovered>";
const wstring LINESPARTIALLYCOVERED = L"<LinesPartiallyCovered>";
const wstring LINESPARTIALLYCOVEREDEND = L"</LinesPartiallyCovered>";
const wstring LINESNOTCOVERED = L"<LinesNotCovered>";
const wstring LINESNOTCOVEREDEND = L"</LinesNotCovered>";
const wstring BLOCKSCOVERED = L"<BlocksCovered>";
const wstring BLOCKSCOVEREDEND = L"</BlocksCovered>";
const wstring BLOCKSNOTCOVERED = L"<BlocksNotCovered>";
const wstring BLOCKSNOTCOVEREDEND = L"</BlocksNotCovered>";

const wstring LNSTART = L"<LnStart>";
const wstring LNSTARTEND = L"</LnStart>";
const wstring LINES = L"<Lines>";
const wstring LINESEND = L"</Lines>";
const wstring LNEND = L"<LnEnd>";
const wstring LNENDEND = L"</LnEnd>";
const wstring COVERAGE = L"<Coverage>";
const wstring COVERAGEEND = L"</Coverage>";

CParseCoverageData::CParseCoverageData(void)
:m_hFile(NULL),
m_pHead(NULL)
{
}

CParseCoverageData::~CParseCoverageData(void)
{
	Close();
}

void CParseCoverageData::Close()
{
	if(m_pHead != NULL)
		UnmapViewOfFile(m_pHead);
	m_pHead = NULL;

	if(m_hFile != NULL)
		CloseHandle(m_hFile); 
	m_hFile = NULL;
}

void CParseCoverageData::Open(LPCTSTR lpszFilePath)
{
	Close();

	HANDLE fp = CreateFile(lpszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, 
		OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

	if(fp == NULL) 
		return; 

	DWORD dwBytesHigh = 0;
	DWORD dwBytesInBlock = GetFileSize(fp, &dwBytesHigh);

	m_hFile = CreateFileMapping(fp, NULL, PAGE_READONLY, dwBytesHigh, dwBytesInBlock, NULL);
	if(m_hFile == NULL)
		return;

	CloseHandle(fp); 

	__int64 qwFileOffset = 0; 
	m_pHead = (LPVOID)MapViewOfFile(m_hFile, FILE_MAP_READ, (DWORD)(qwFileOffset >> 32), 
		(DWORD)(qwFileOffset & 0xFFFFFFFF), dwBytesInBlock);
}

BOOL CParseCoverageData::CheckFilter(const wstring& sFilePath, FilterRule filterType, WSTRINGLIST& filters)
{
	WSTRINGLIST::iterator it = filters.begin();
	for(; it != filters.end(); it++)
	{
		if(sFilePath.find(*it) != wstring::npos)
		{
			if(filterType == eRuleExclude)
				return FALSE;
			else
				return TRUE;
		}
	}
	if(filterType == eRuleExclude)
		return TRUE;
	else
		return FALSE;
}

void CParseCoverageData::ListFiles(FilterRule filterType, WSTRINGLIST& filters, FILELIST& outFiles)
{
	WCHAR* sNext = (WCHAR*)m_pHead;
	WCHAR* sClose = NULL;
	while(sNext != NULL)
	{
		sNext = wcsstr(sNext, SOURCEFILENAMES.c_str());
		if(sNext == NULL)
			break;

		sClose = wcsstr(sNext, SOURCEFILENAMESEND.c_str());
		if(sClose == NULL)
			break;

		wstring sFileID = GetFirstChildValue(sNext, sClose, SOURCEFILEID, SOURCEFILEIDEND);
		wstring sFileName = GetFirstChildValue(sNext, sClose, SOURCEFILENAME, SOURCEFILENAMEEND);
		if(CheckFilter(sFileName, filterType, filters))
		{
			int nFileID = _wtoi(sFileID.c_str());
			outFiles.push_back(make_pair(sFileName, nFileID));
#ifdef _DEBUG
			wprintf(L"%d\t%s\r\n", nFileID, sFileName.c_str());
#endif
		}

		sNext = sClose + SOURCEFILENAMESEND.length();
	}
}

wstring CParseCoverageData::GetFirstChildValue(WCHAR* lpStart, 
											   WCHAR* lpEnd, 
											   const wstring& sTagBegin,
											   const wstring& sTagEnd)
{
	wstring sRet;
	if(lpStart == NULL || lpEnd == NULL)
		return sRet;

	WCHAR* pBegin = wcsstr(lpStart, sTagBegin.c_str());
	WCHAR* pEnd = wcsstr(pBegin, sTagEnd.c_str());
	if(pBegin == NULL || pEnd == NULL)
		return sRet;

	if(pBegin >= lpEnd || pEnd >= lpEnd)
		return sRet;

	if(pEnd <= pBegin + sTagBegin.length())
		return sRet;

	sRet.assign(pBegin + sTagBegin.length(), pEnd - pBegin - sTagBegin.length());
	return sRet;
}

void CParseCoverageData::GetFileCoverage(const wstring& sFilePath, 
										 int nFileID, 
										 LINELIST& lines, 
										 COVERAGE_DATA* pCoverage)
{
	WCHAR* sNext = (WCHAR*)m_pHead;
	WCHAR* sClose = NULL;
	while(sNext != NULL)
	{
		sNext = wcsstr(sNext, METHOD.c_str());
		if(sNext == NULL)
			break;

		sClose = wcsstr(sNext, METHODEND.c_str());
		if(sClose == NULL)
			break;

		wstring sFileID = GetFirstChildValue(sNext, sClose, SOURCEFILEID, SOURCEFILEIDEND);
		int nFileIDIt = _wtoi(sFileID.c_str());
		if(nFileID == nFileIDIt)
		{
			if(pCoverage != NULL)
			{
				wstring sCovered = GetFirstChildValue(sNext, sClose, LINESCOVERED, LINESCOVEREDEND);
				wstring sNotCovered = GetFirstChildValue(sNext, sClose, LINESNOTCOVERED, LINESNOTCOVEREDEND);
				wstring sPartiallyCovered = GetFirstChildValue(sNext, sClose, LINESPARTIALLYCOVERED, LINESPARTIALLYCOVEREDEND);
				wstring sBlockCovered = GetFirstChildValue(sNext, sClose, BLOCKSCOVERED, BLOCKSCOVEREDEND);
				wstring sBlockNotCovered = GetFirstChildValue(sNext, sClose, BLOCKSNOTCOVERED, BLOCKSNOTCOVEREDEND);

				pCoverage->nBlocksCovered += _wtoi(sBlockCovered.c_str());
				pCoverage->nBlocksNotCovered += _wtoi(sBlockNotCovered.c_str());
				pCoverage->nLinesCovered += _wtoi(sCovered.c_str());
				pCoverage->nLinesNotCovered += _wtoi(sNotCovered.c_str());
				pCoverage->nLinesPartiallyCovered += _wtoi(sPartiallyCovered.c_str());
			}

			EnumerateLines(sNext, sClose, lines);
		}

		sNext = sClose + METHODEND.length();
	}
}

void CParseCoverageData::EnumerateLines(WCHAR* lpStart, WCHAR* lpEnd, LINELIST& lines)
{
	if(lpStart == NULL || lpEnd == NULL)
		return;

	WCHAR* sNext = (WCHAR*)lpStart;
	WCHAR* sClose = NULL;
	while(sNext != NULL && sNext < lpEnd)
	{
		sNext = wcsstr(sNext, LINES.c_str());
		if(sNext == NULL || sNext >= lpEnd)
			break;

		sClose = wcsstr(sNext, LINESEND.c_str());
		if(sClose == NULL || sClose >= lpEnd)
			break;

		wstring sLnStart = GetFirstChildValue(sNext, sClose, LNSTART, LNSTARTEND);
		wstring sLnEnd = GetFirstChildValue(sNext, sClose, LNEND, LNENDEND);
		wstring sCoverage = GetFirstChildValue(sNext, sClose, COVERAGE, COVERAGEEND);
		int nLnStart = _wtoi(sLnStart.c_str());
		int nLnEnd = _wtoi(sLnEnd.c_str());
		int nCoverage = _wtoi(sCoverage.c_str());
		for(int i = nLnStart; i <= nLnEnd; i++)
		{
			//nCoverage == 2, not covered.
			if(nCoverage == 0)
				lines[i] = eCovered;
			else if(nCoverage == 1)
				lines[i] = ePartiallyCovered;
		}

		sNext = sClose + LINESEND.length();
	}
}
